package cn.zyq.charging.cost.dao.repository.impl;

import cn.zyq.charging.common.pojo.utils.JsonUtils;
import cn.zyq.charging.cost.dao.repository.CostRuleCacheRepository;
import cn.zyq.charging.cost.pojo.po.CostRulePO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.function.Consumer;

@Slf4j
@Repository
public class CostRuleCacheRepositoryImpl implements CostRuleCacheRepository {

    @Autowired
    private RedisTemplate<String, Serializable> redisTemplate;
    //注意：
    //1.当  RedisTemplate  redisTemplate 不写泛型时，它的默认泛型是<Object, Object> ，这时候springBoot可以自动转入成功
    //2.当  RedisTemplate<String, Serializable>  redisTemplate 写很具体的特殊泛型时， 必须在配置类中自己手动实例化RedisTemplate,并指定KeySerializer和ValueSerializer。



    /**
     *
     *  从redis中根据充电站id查询计价规则:
     * (CostController中的计价方法calculateCost()调用了getMatchCostRuleByTime()，getMatchCostRuleByTime()调用了getCostRule()，getCostRule()中调用了本方法getCostRuleByStationId()):
     *     在CostServiceImpl中的getCostRule()方法中
     *     1.先用costRuleCacheRepository.getCostRuleByStationId(stationId)去redis中获取计价规则
     *     2.如果从redis中获取到了数据，则直接返回。
     *     3.如果redis中没有数据，则用costRuleRepository.getCostRuleByStationId(stationId)从mysql数据库中获取计价规则。
     *     4.如果获取到了就再调用本方法saveCostRule()将获取的计价规则存入到redis中。
     * 通过站点id从redis查询计价规则
     * @param stationId
     * @return
     */
    public List<CostRulePO>  getCostRuleByStationId(Integer stationId){
        ValueOperations<String, Serializable>  valueOperations=redisTemplate.opsForValue();
        String costRuleKey="COST_RULE_KEY_"+stationId;
        Serializable serializable = valueOperations.get(costRuleKey);
        //serializable=[{"id":1,"stationId":5,"gunType":1,"name":"尖上午","startTime":8,"endTime":12,"powerFee":1.50,"serviceFee":0.40},{"id":2,"stationId":5,"gunType":1,"name":"尖下午","startTime":14,"endTime":18,"powerFee":1.50,"serviceFee":0.40},{"id":3,"stationId":5,"gunType":1,"name":"峰","startTime":18,"endTime":24,"powerFee":1.40,"serviceFee":0.40},{"id":4,"stationId":5,"gunType":1,"name":"平","startTime":12,"endTime":14,"powerFee":1.30,"serviceFee":0.40},{"id":5,"stationId":5,"gunType":1,"name":"谷","startTime":0,"endTime":8,"powerFee":1.00,"serviceFee":0.40}]
        log.debug("CostRuleCacheRepositoryImpl--getCostRuleByStationId()通过站点id:{},从Redis获取计价规则:{}", stationId, serializable);
        if(serializable!=null){
            return getCostRulePOS(serializable);
        }
        return Collections.emptyList();
    }


    /**
     * 本方法是给上边的getCostRuleByStationId()方法调用的:
     * 将从redis获取的json字符串转化为CostRulePO存到List集合中返回。 可以自己了解转化工具： https://hutool.cn/
     * @param serializable
     * @return
     */
    private List<CostRulePO> getCostRulePOS(Serializable serializable){
        //[CostRuleA,CostRuleB,CostRuleC] 不能直接转换为 List<CostRule>
        List<CostRulePO> result=new ArrayList<CostRulePO>();

        //1.用fasterxml.jackson.databind.ObjectMapper的readValue()将Json转化为一个List集合
        // (list里边存储了多个LinkedHashMap对象<每一个LinkedHashMap都是一个CostRulePO对象>)
        // 因为需要让CostRulePO对象的每个属性都排序，所以用了LinkedHashMap
        List<LinkedHashMap<String,CostRulePO>> list =
                    JsonUtils.fromJsonToObject(String.valueOf(serializable), List.class);
        /*list.forEach(new Consumer<LinkedHashMap<String, CostRulePO>>() {
            public void accept(LinkedHashMap<String, CostRulePO> CostRulePOLinkedHashMap) {
                CostRulePO costRulePO = JsonUtils.convertValue(CostRulePOLinkedHashMap, CostRulePO.class);
                //3.将CostRule对象添加到List集合中
                result.add(costRulePO);
            }
        });*/
        list.forEach(linkedHashMap->{
            //2.将一个linkedHashMap集合转化为一个CostRule对象
            //CostRulePO costRulePO = JsonUtils.fromJsonToObject(String.valueOf(linkedHashMap), CostRulePO.class); 这样讲json转化为CostRulePO会失败
            //所以要改成下边的写法。
            //JsonUtils.convertValue(对象， 最终的类型)
            CostRulePO costRulePO = JsonUtils.convertValue(linkedHashMap, CostRulePO.class);
            //3.将CostRule对象添加到List集合中
            result.add(costRulePO);
        });
        return result;
    }



    /**
     * (计价方法calculateCost()调用了getMatchCostRuleByTime()，getMatchCostRuleByTime()调用了getCostRule()，getCostRule()中调用了本方法saveCostRule()):
     * 在CostServiceImpl中的getCostRule()方法中:
     *     1.先用costRuleCacheRepository.getCostRuleByStationId(stationId)去redis中获取计价规则
     *     2.如果从redis中获取到了数据，则直接返回。
     *     3.如果redis中没有数据，则用costRuleRepository.getCostRuleByStationId(stationId)从mysql数据库中获取计价规则。
     *     4.如果获取到了就再调用本方法saveCostRule()将获取的计价规则存入到redis中。
     * 保存计价规则到redis
     * @param stationId
     * @param costRulePOS
     */
    public void saveCostRule(Integer stationId,  List<CostRulePO> costRulePOS){
        //1.用opsForValue()方法获取ValueOperations对象
        ValueOperations<String,Serializable> valueOperations=redisTemplate.opsForValue();
        //2.准备key-value
        String costRuleKey = "COST_RULE_KEY_" + stationId;//key
        String json=JsonUtils.toJson(costRulePOS);//value
        //3.将key-value保存到redis中
        valueOperations.set(costRuleKey,json);
    }
    //redisTemplate.opsForValue(): 用于操作 Redis 中的字符串类型（String）。它返回一个 ValueOperations 对象，可以用于执行各种与字符串相关的操作。
    //以下是常用的方法：
    //方法                        说明
    //set(K key, V value):  设置指定 key 的值为 value
    //set(K key, V value, long timeout, TimeUnit unit): 设置指定 key 的值为 value，并设置过期时间
    //get(Object key):  获取指定 key 的值
    //getAndSet(K key, V value):  获取指定 key 的旧值，并设置新的值
    //increment(K key, long delta):  将 key 所对应的值增加 delta（仅适用于数值类型）
    //decrement(K key, long delta):  将 key 所对应的值减少 delta（仅适用于数值类型）
    //示例代码
    //// 存储数据
    //redisTemplate.opsForValue().set("user:1001", user);
    //// 获取数据
    //User user = (User) redisTemplate.opsForValue().get("user:1001");
    //// 带过期时间存储数据
    //redisTemplate.opsForValue().set("token:abc123", "exampleToken", 60, TimeUnit.SECONDS);
    //// 数值递增
    //Long count = redisTemplate.opsForValue().increment("counter", 1);
}
